package extraction;

import java.util.ArrayList;
import java.util.Date;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.StringTokenizer;

public class DataExtraction {
	
	/**
	 * @param ficoutPath chemin d'acces au dossier contenant le fichier de donnees 
	 * @param dataDepth profondeur des donnees a garder, en minute
	 * @param missingDuration temps a partir duquel on considere une donnee en retard comme manquante, en minute
	 * @throws ParseException 
	 * @throws IOException 
	 * */
	public DataExtraction(String ficoutLocalPath, int dataDepth, int missingDuration) throws IOException, ParseException{	
		//Recuperation des chemins et des constantes
		this.ficoutLocalPath = ficoutLocalPath;
		this.missingDuration = missingDuration;
		
		//Recuperation du nom des parametres dans l'en-tete
		getHeader(this.ficoutLocalPath);
		
		//Initialisation des objets de calcul
		maxima = new Maxima(dataName);
		cumul = new Cumul(dataName);
		
		//Remplissage initial de la base de donnee
		dtb = new Database(dataDepth);
		addFirstData();
		refresh();
	}
	
	//Ajout de la premiere donnees a la base et initialisation de parametres utiles pour la suite
	private void addFirstData() throws IOException, ParseException{
		
		//Creation et initialisation de variables de la methode
		ArrayList<String> value = new ArrayList<String>();
		ArrayList<Boolean> validity = new ArrayList<Boolean>();
		boolean globalValidity = true;
		Date date;
		String valueTemp;
		
		ips = new FileInputStream(ficoutLocalPath);
		ipsr = new InputStreamReader(ips);
		br = new BufferedReader(ipsr);
		
		//Test de la presence de donnees et suppression de l en-tete
		if(br.readLine() == null){
			System.err.println("fic.out est corrompu");
			return;
		}
		
		//Initialisation de variables de la classe
		st = new StringTokenizer(br.readLine(), " ");
		date = sdf.parse(st.nextToken() + " " + st.nextToken());//erreur a gerer
		lastValidDate = date;
		
		while(st.hasMoreTokens()){
			valueTemp = st.nextToken().replace(",", ".");
			value.add(valueTemp);
			if(valueTemp.compareTo("/") == 0){
				validity.add(false);
				globalValidity = false;
				missingValueCount.add(1);
			} else {
				validity.add(true);
				missingValueCount.add(0);
			}
		}
		dtb.addObs(date, new Obs(value, validity, globalValidity));
	}
	
	//Analyse des donnees et ajout dans la base
	public void refresh() throws IOException, ParseException{
	
		//Initialisation des variables
		boolean newValidDate = false;
		Date dateTemp;
		ips = new FileInputStream(ficoutLocalPath);
		ipsr = new InputStreamReader(ips);
		br = new BufferedReader(ipsr);
		
		if(br.readLine() == null){//En-tete
			newValidDate = false;
			System.err.println("fic.out est corrompu");
		} else {
			while ((line = br.readLine())!= null){
				ArrayList<String> value = new ArrayList<String>();
				ArrayList<Boolean> validity = new ArrayList<Boolean>();
				boolean globalValidityTemp = true;
				
				//Parsage des donnees
				st = new StringTokenizer(line, " ");
				String temp;
				
				dateTemp = sdf.parse(st.nextToken() + " " + st.nextToken());//erreur a gerer
				
				while(st.hasMoreTokens()){
					temp = st.nextToken().replace(",", ".");
					value.add(temp);
					if(temp.compareTo("/") == 0){
						validity.add(false);
						globalValidityTemp = false;
						missingValueCount.set(validity.size() - 1, missingValueCount.get(validity.size() - 1) + 1);
					} else {
						validity.add(true);
						missingValueCount.set(validity.size() - 1, 0);
					}
				}
				
				//Ajout a la base de donnees
				if(dateTemp.after(lastValidDate) && dtb.hasDateExist(dateTemp) == false){
					dtb.addObs(dateTemp, new Obs(value, validity, globalValidityTemp));
				}else if(dateTemp.after(lastValidDate) && dtb.hasDateExist(dateTemp) == true){
					dtb.setObs(dateTemp, new Obs(value, validity, globalValidityTemp));
				} else if(!dateTemp.after(lastValidDate) 
						&& dtb.hasDateExist(dateTemp) == true 
						&& dtb.getObs(dateTemp).getGlobalValidity() == false){
					dtb.setObs(dateTemp, new Obs(value, validity, globalValidityTemp));
				}
				
				
				//Derniere donnee exploitable
				if(true == globalValidityTemp && dateTemp.after(lastValidDate)){
					newValidDate = true;
					lastValidDate = dateTemp;
				} 
				else if(false == globalValidityTemp && dateTemp.after(lastValidDate)){
					newValidDate = true;
					for(int i = 0; i < validity.size(); i++){
						if(missingValueCount.get(i) > 0 && missingValueCount.get(i) <= missingDuration){
							newValidDate = false;
							break;
						}
					}
					if(true == newValidDate){
						lastValidDate = dateTemp;
					}
				}
				if(true == newValidDate){
					dtb.resizeDatabase();
					maxima.refresh(dtb.getObs(lastValidDate), lastValidDate);
				}
			}
			br.close();
		}
	}
	
	//Recuperation des noms des parametres dans l'en-tete du fichier de donnees
	private void getHeader(String ficoutPath){
		try {
			ips = new FileInputStream(ficoutPath);
		} catch (FileNotFoundException e) {
			System.err.println("Impossible de trouver le fichier de donnees");
//			e.printStackTrace();
		} 
		ipsr = new InputStreamReader(ips);
		br = new BufferedReader(ipsr);
		try {
			st = new StringTokenizer(br.readLine(), " ");
			br.close();
		} catch (IOException e) {
			System.err.println("Impossible de lire le fichier de donnees");
//			e.printStackTrace();
		}
		
		//"DATE" non lu
		st.nextToken(); //dd/mm/yy
		st.nextToken(); //hh:mm
			
		while(st.hasMoreTokens()){ //Autres types
			dataName.add(st.nextToken());
		}
	}
	
	
	public String getLastValue(String name){
		return dtb.getObs(lastValidDate).getValue(dataName.indexOf(name));
	}
	
	public Date getLastDate(){
		return lastValidDate;
	}
	
	public boolean getLastValidity(String name){
		return dtb.getObs(lastValidDate).getValidity(dataName.indexOf(name));
	}
	
	public String getValue(String name, Date date){
		return dtb.getObs(date).getValue(dataName.indexOf(name));
	}
	
	public boolean getValidity(String name, Date date){
		return dtb.getObs(date).getValidity(dataName.indexOf(name));
	}
	
//	private int getMissingValueCount(int index){
//		return missingValueCount.get(index);
//	}
	
	public void enableMaxima(String name, int resetMinHour, int resetMaxHour){
		maxima.add(name, lastValidDate, dtb, resetMinHour, resetMaxHour);
	}
	
	public String getMin(String name){
		return maxima.getMin(name);
	}
	
	public String getMax(String name){
		return maxima.getMax(name);
	}
	
	public void enableCumul(String name, int resetHour){
		cumul.add(name, lastValidDate, dtb, resetHour);
	}
	
	public String getCumul(String name){
		return cumul.getCumul(name);
	}
	
	private ArrayList<String> dataName = new ArrayList<String>();
	private ArrayList<Integer> missingValueCount = new ArrayList<Integer>();
	private Cumul cumul;
	private Database dtb;
	private Date lastValidDate;
	private BufferedReader br;
	private InputStream ips;
	private InputStreamReader ipsr;
	private int missingDuration;
	private Maxima maxima;
	private SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yy HH:mm");
	private String ficoutLocalPath, line;
	private StringTokenizer st;
}
